package com.zbystudy;

import java.util.*;

/**
 * Created By zby on 21:01 2019/1/9
 * poi的map操作,为了poi操作方便，特地重写了Map方法
 * 不允许有重复的值
 */

public final class PoiHashMap<K, V> implements PoiMap<K, V> {

    private int size = 0;

    private int capacity = 10;

    private Node<K, V>[] table;

    private Collection<V> values;

    private Set<Entry<K, V>> entrySet;

    /**
     * keySet集合
     */
    private Set<K> keySet;

    public PoiHashMap() {
        table = new Node[this.capacity];
    }

    public PoiHashMap(int capacity) {
        this();
        if (capacity <= 0) {
            throw new IllegalArgumentException("capacity不能小于等于0");
        }
        this.capacity = capacity;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return 0 == size ? true : false;
    }

    @Override
    public boolean containsKey(Object key) {
        if (isEmpty())
            return false;
        for (K k : keySet()) {
            boolean isEquals = Objects.equals(key, k);
            if (isEquals)
                return true;
        }
        return false;
    }


    @Override
    public boolean containsValue(Object value) {
        if (isEmpty())
            return false;
        for (K k : keySet) {
            V val = get(k);
            boolean isEquals = Objects.equals(val, value);
            if (isEquals)
                return true;
        }
        return false;
    }

    @Override
    public V get(Object key) {
        if (isEmpty())
            throw new NullPointerException("当前map为空this=" + this);
        if (!containsKey(key))
            throw new Error("集合中不存在这个key=" + key + "的值");
        for (int i = 0; i < table.length; i++) {
            Node node = table[i];
            if (node == null)
                throw new NullPointerException("第“" + i + "”个节点为空node=" + node);
            K k = (K) node.getKey();
            if (null == k)
                throw new NullPointerException("空指针异常，第“" + i + "”个节点的key值为空key=" + key);
            if (null == key || key.getClass() != k.getClass()) return null;
            if (Objects.equals(k, key)) {
                return (V) node.getValue();
            }
        }
        return null;
    }

    /**
     * 判断hash冲突
     *
     * @param key
     * @param value
     * @return
     */
    @Override
    public PoiMap put(K key, V value) {
        //判断K V 都不能为空
        if (null == key)
            throw new NullPointerException("key值不能为空key=" + key);
        if (null == value)
            throw new NullPointerException("value值不能为空value=" + value);
        //判断K V是否有重复的值
        if (containsKey(key))
            throw new IllegalArgumentException("key值重复key=" + key);
        if (containsValue(value))
            throw new IllegalArgumentException("value值重复value=" + value);
        //添加数据
        if (capacity > this.size) {
            Node<K, V> node = new Node<K, V>(key, value);
            table[size] = node;
            size++;
        } else {
            capacity = capacity << 1 + 2;
            table = Arrays.copyOf(table, capacity);
        }
        return this;
    }

    @Override
    public V remove(Object key) {
        if (isEmpty())
            throw new IllegalArgumentException("集合为空，参数有误");
        if (null == key)
            throw new IllegalArgumentException("key不能为空");
        if (!containsKey(key))
            throw new Error("集合中不存在这个key=" + key + "的值，请确认数据后移除");
        capacity = size + 2;
        table = Arrays.copyOf(table, size);
        int length = table.length;
        for (int i = 0; i < length; i++) {
            if (Objects.equals(table[i].key, key)) {
                V value = table[i].getValue();
                if (i == length - 1) {
                    table = Arrays.copyOf(table, length - 1);
                } else {
                    for (int j = i; j < length - 1; j++) {
                        table[j] = table[j + 1];
                    }
                }
                size--;
                return value;
            }
        }
        return null;
    }

    @Override
    public PoiMap<K, V> putAll(PoiMap<K, V> m) {
        if (m.isEmpty())
            throw new IllegalArgumentException("空指针异常");
        Set<Entry<K, V>> entries = m.entrySet();
        for (Entry<? extends K, ? extends V> en : entries) {
            put((K) en.getKey(), (V) en.getValue());
        }
        return this;
    }

    @Override
    public PoiMap clear() {
        if (!isEmpty()) {
            for (Node<K, V> node : table) {
                node = null;
            }
        }
        size = 0;
        capacity = 10;
        return this;
    }

    @Override
    public Set<K> keySet() {
        if (isEmpty())
            return null;
        //创建临时节点数组
        keySet = new HashSet<>(size);
        Node[] tem = Arrays.copyOf(table, size);
        for (Node<K, V> node : tem) {
            keySet.add(node.getKey());
        }
        return keySet;
    }

    /**
     * Created By zby on 0:57 2019/1/11
     * 因为Values类继承了AbstractCollection抽象类，
     * AbstractCollection继承了Collection接口，
     * 直接返回接口的实现类即可，接口名称指向Values对象
     * 这也就是为什么接口引用指向实现类的原因
     */
    @Override
    public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values<>();
            values = vs;
        }
        return vs;
    }

    /**
     * Created By zby on 1:24 2019/1/11
     * 当前Entry就是存储Map的节点值
     */
    @Override
    public Set<Entry<K, V>> entrySet() {
        if (isEmpty())
            return null;
        Set<Entry<K, V>> entries = entrySet;
        if (null == entries) {
            entries = new HashSet<>();
        }
        for (Node<K, V> entry : table) {
            entries.add(entry);
        }
        entrySet = entries;
        return entries;
    }

    /**
     * @param <K> 泛型 k表示key值
     * @param <V> V表示value值
     * @author zhubaoya
     * @description 有可能会出现hash冲突，应该需要避免hash冲突
     */
    static class Node<K, V> implements PoiMap.Entry<K, V> {

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public Node() {
        }

        private K key;

        private V value;

        @Override
        public K getKey() {
            return key;
        }

        @Override
        public V getValue() {
            return value;
        }

        /**
         * Created By zby on 22:15 2019/1/9
         * 必须重写equals方法，
         * 1、o的指向引用的首地址和当前对象的首地址相同，返回true
         * 2、判断两者的类是否是同一个类
         * 3、判断o当前类实现的接口的实例化对象，如果是需要转型为接口，因为接口最终还是指向其实现类
         *
         * @param o
         */
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (null == o || this.getClass() != o.getClass()) return false;
            if (o instanceof Map.Entry) {
                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
                if (Objects.equals(key, entry.getKey())
                        || Objects.equals(value, entry.getValue())) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hash(key, value);
        }

        @Override
        public String toString() {
            return "Node{" + "key=" + key + ", value=" + value + '}';
        }
    }

    /**
     * Created By zby on 1:09 2019/1/10
     * 获取values值
     */
    final class Values<V> extends AbstractCollection<V> {

        private int count;

        public Values() {
            count = size;
        }

        /**
         * 通过创建匿名类来获取自定义的迭代器
         *
         * @return
         */
        @Override
        public Iterator iterator() {
            return new Iterator() {

                @Override
                public boolean hasNext() {
                    return 0 != count ? true : false;
                }

                @Override
                public Object next() {
                    if (isEmpty())
                        throw new Error("空指针异常");
                    if (count >= 0) {
                        return table[--count].value;
                    }
                    return null;
                }
            };
        }

        /**
         * 如果使用嵌套类，也就是静态内部类，是无法获取外部类普通方法和普通属性，
         * 更不用说返回外部类对象的引用，因而使用普通内部类，可以访问外部类的属性和方法，
         * 就相当于JavaScript中的闭包的概念，也可以通过outerClass.this创建外部类的引用。
         *
         * @return
         */
        @Override
        public int size() {
            return count;
        }
    }
    
}
